package com.dnndo.common.utils.bean;

import com.dnndo.common.exception.CustomExceptions;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cglib.beans.BeanCopier;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * 高效Bean转换工具类（基于CGLIB实现）
 * <p>
 * 功能特性：
 * 1. 支持单个对象、List集合、Page分页对象的属性拷贝
 * 2. 使用BeanCopier缓存机制提升性能
 * 3. 线程安全设计
 * 4. 完善的空值处理和异常处理
 *
 * @author dnndo
 * @version 1.0.0
 * @since 2025年4月26日
 */
@Slf4j
public final class BeanDtoVoUtil {

    /**
     * BeanCopier缓存池（线程安全）
     * Key格式：源类全限定名 + "->" + 目标类全限定名
     */
    private static final ConcurrentHashMap<String, BeanCopier> BEAN_COPIER_CACHE = new ConcurrentHashMap<>();

    private BeanDtoVoUtil() {
        throw new UnsupportedOperationException("工具类禁止实例化");
    }

    /**
     * 对象属性拷贝（支持深拷贝配置）
     *
     * @param source 源对象（非null）
     * @param targetClass 目标类类型（非null）
     * @param <E> 目标类型
     * @return 目标对象实例
     * @throws IllegalStateException 当对象创建失败时抛出
     */
    public static <E> E convert(Object source, Class<E> targetClass) {
        if (source == null || targetClass == null) {
            return null;
        }

        try {
            E target = targetClass.getDeclaredConstructor().newInstance();
            BeanCopier copier = getBeanCopier(source.getClass(), targetClass);
            copier.copy(source, target, null);
            return target;
        } catch (ReflectiveOperationException e) {
            String errorMsg = String.format("对象转换失败 [%s -> %s]：%s",
                    source.getClass().getSimpleName(),
                    targetClass.getSimpleName(),
                    e.getMessage());
            log.error(errorMsg, e);
            throw new CustomExceptions("服务异常，请稍后重试",500);
        }
    }
    /**
     * 集合对象转换
     */
    public static <T, V> List<V> listVo(List<T> sourceList, Class<V> targetClass) {
        //判断集合是否为空
        if (sourceList == null || sourceList.isEmpty()) {
            return Collections.emptyList();
        }
        //判断集合大小，根据集合大小选择转换方式
        if (sourceList.size() < 5000) {
            return convertList(sourceList, targetClass);
        } else {
            return convertListParallel(sourceList, targetClass);
        }
    }


    /**
 * 集合对象转换（标准流方式）
 *
 * @param sourceList 源集合（允许为null）
 * @param targetClass 目标类类型
 * @param <T> 源数据类型
 * @param <V> 目标数据类型
 * @return 非空目标集合（输入为空时返回空集合）
 */
private static <T, V> List<V> convertList(List<T> sourceList, Class<V> targetClass) {
    if (sourceList == null || sourceList.isEmpty()) {
        return Collections.emptyList();
    }

    final int BATCH_SIZE = 2000;
    return IntStream.range(0, (sourceList.size() + BATCH_SIZE - 1) / BATCH_SIZE)
        .mapToObj(i -> sourceList.subList(i * BATCH_SIZE,
                Math.min((i + 1) * BATCH_SIZE, sourceList.size())))
        .flatMap(batch -> batch.stream().map(item -> convert(item, targetClass)))
        .collect(Collectors.toList());
}

/**
 * 集合对象转换（并行流方式，推荐万级以上数据量使用）
 *
 * @param sourceList 源集合（允许为null）
 * @param targetClass 目标类类型
 * @param <T> 源数据类型
 * @param <V> 目标数据类型
 * @return 非空目标集合（输入为空时返回空集合）
 */
private static <T, V> List<V> convertListParallel(List<T> sourceList, Class<V> targetClass) {
    if (sourceList == null || sourceList.isEmpty()) {
        return Collections.emptyList();
    }

    final int BATCH_SIZE = 4000;
    return IntStream.range(0, (sourceList.size() + BATCH_SIZE - 1) / BATCH_SIZE)
        .parallel()
        .mapToObj(i -> sourceList.subList(i * BATCH_SIZE,
                Math.min((i + 1) * BATCH_SIZE, sourceList.size())))
        .flatMap(batch -> batch.stream().map(item -> convert(item, targetClass)))
        .collect(Collectors.toList());
}


    /**
     * 获取或创建BeanCopier实例
     */
    private static BeanCopier getBeanCopier(Class<?> sourceClass, Class<?> targetClass) {
        String cacheKey = generateCacheKey(sourceClass, targetClass);
        return BEAN_COPIER_CACHE.computeIfAbsent(cacheKey, key ->
            BeanCopier.create(sourceClass, targetClass, false)
        );
    }

    /**
     * 生成缓存键（源类全名->目标类全名）
     */
    private static String generateCacheKey(Class<?> source, Class<?> target) {
        return source.getName() + "->" + target.getName();
    }
}
